﻿using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace Misc
{
	#region Interfaces/Contracts

	public interface IEntity
	{
		int Id { get; }
		string Name { get; }
	}

	public interface IManagement<T> : IDisposable
	{
		T Get(int id);
	}

	#endregion

	#region Derived entity classes

	public class Foo : IEntity
	{
		private readonly int _id;

		public Foo(int id)
		{
			_id = id;
		}

		public int Id { get { return _id; } }
		public string Name { get { return "Foo"; } }
	}

	public class Bar : IEntity
	{
		private readonly int _id;

		public Bar(int id)
		{
			_id = id;
		}

		public int Id { get { return _id; } }
		public string Name { get { return "Bar"; } }
	}

	#endregion

	#region Derived managment classes

	public class FooService : IManagement<Foo>
	{
		public FooService()
		{
		}

		public Foo Get(int id)
		{
			return new Foo(id);
		}

		public void Dispose()
		{
		}
	}

	public class BarService : IManagement<Bar>
	{
		public BarService()
		{
		}

		public Bar Get(int id)
		{
			return new Bar(id);
		}

		public void Dispose()
		{
		}
	}

	#endregion

	[TestFixture]
	public class LazyConstructionTests
	{
		[Test]
		public void EnsureThatClassesAreNotConstructedUntilNeeded()
		{
			var d = new Dictionary<Type, Func<int, IEntity>>
			        	{
			        		{typeof (Foo), id => GetEntity(() => new FooService(), id)}, 
							{typeof (Bar), id => GetEntity(() => new BarService(), id)}
			        	};

			const int fooId = 1;
			var foo = d[typeof(Foo)].Invoke(fooId);
			Assert.AreEqual(typeof(Foo), foo.GetType());
			Assert.AreEqual(fooId, foo.Id);
			Assert.AreEqual("Foo", foo.Name);
			
			const int barId = 1;
			var bar = d[typeof(Bar)].Invoke(barId);
			Assert.AreEqual(typeof(Bar), bar.GetType());
			Assert.AreEqual(barId, bar.Id);
			Assert.AreEqual("Bar", bar.Name);
		}


		private static T GetEntity<T>(Func<IManagement<T>> management, int id)
		{
			T entity;
			using (var man = management.Invoke())
				entity = man.Get(id);

			return entity;
		}
	}
}
